// @Creation:	27.04.2012
// @Version:	27.04.2012
// 
// @Contents:	serviceImpl.go 
//	Реализация простенького сервиса		   
//
// @cpu:	x86,
// @author:	alexey kurshakov [alexey.kurshakov@gmail.com]
//

package service

import (
	"errors"
	"sync/atomic"
	"time"
)

// Ждем событие в течение некоторого времени, иначе выходим по таймауту
func waitForEvent(e chan int, timeOutMs int) (eventId int, err error) {
	if timeOutMs <= 0 {
		eventId = <-e
	} else {
		select {
		case eventId = <-e:
		case <-time.After(time.Duration(timeOutMs) * time.Millisecond):
			eventId = -1
			err = errors.New("timeout exceeded during wait event")
		}
	}
	return
}

// Реализация сервиса

// Запустить сервис
func (s *SimpleService) Start() (err error) {
	// Читаем текущее состояние сервиса
	if s.GetState() != StateStopped {
		// если сервис уже запущен то выходим с ошибкой	
		err = errors.New(s.GetName() + " is not stopped")
	} else {
		// иначе запускаем главную работающую функцию сервиса в отдельном потоке
		go s.ServiceMain()
	}
	return
}

// Остановить сервис
func (s *SimpleService) Stop() (err error) {
	// Читаем текущее состояние сервиса
	if s.GetState() == StateStopped {
		// если сервис не запущен то выходим с ошибкой
		err = errors.New(s.GetName() + " is yet stopped")
	} else {
		// иначе посылаем запрос на завершение сервиса
		s.SendEvent(EventExitReq)
	}
	return
}

// Установить новое состояние для сервиса
func (s *SimpleService) SetState(newState int) {
	atomic.StoreInt32(&s.currentState, int32(newState))
}

// Получить текущее состояние сервиса
func (s *SimpleService) GetState() int {
	return int(atomic.LoadInt32(&s.currentState))
}

// Послать событие для обработки в сервис
func (s *SimpleService) SendEvent(eventId int) {
	// Если сервис не запущен, то игнорируем
	if s.GetState() == StateStopped {
		return
	}
	// Если запрошено прерывание сервиса, то устанавливаем
	// соответствующее состояние
	if eventId == EventExitReq || eventId == EventBreakReq {
		s.SetState(StateBreak)
	}
	// Посылаем событие
	s.event <- eventId
}

// Установить обработчик состояний
func (s *SimpleService) SetWorker(worker ServiceWorker) {
	s.mutex.Lock()
	s.worker = worker
	s.mutex.Unlock()
}

// Установливаем сервису имя
func (s *SimpleService) SetName(serviceName string) {
	s.mutex.Lock()
	s.name = serviceName
	s.mutex.Unlock()
}

// Возвращаем имя сервиса
func (s *SimpleService) GetName() string {
	s.mutex.Lock()
	retValue := s.name
	s.mutex.Unlock()
	return retValue
}

// Установить время бездействия для сервиса
func (s *SimpleService) SetSleepTime(sleepTimeMs int) {
	s.mutex.Lock()
	if s.sleepTimeMs <= 0 && sleepTimeMs > 0 {
		s.sleepTimeMs = int32(sleepTimeMs)
		s.SendEvent(EventWake)
	} else {
		s.sleepTimeMs = int32(sleepTimeMs)
	}
	s.mutex.Unlock()
}

// Получить время бездействия для сервиса
func (s *SimpleService) GetSleepTime() int {
	return int(atomic.LoadInt32(&s.sleepTimeMs))
}

// "Главная" управляющая функция сервиса
func (s *SimpleService) ServiceMain() {
	worker := s.worker
	s.event = make(chan int)
	eventId := EventWake

	// На выходе установим состояние сервиса - STATE_STOPPED
	defer func() {
		s.SetState(StateStopped)
		close(s.event)
	}()

	// Инициализируемся, выполняем престартовую работу	
	if worker == nil {
		print(s.GetName() + ": worker is not set, stopped\n")
		return
	}
	err := worker.PreWork()
	if err != nil {
		print(s.GetName() + ": Error during prework\n")
		return
	}
	s.SetState(StateReady)

	// Выполняем рабочий цикл, пока не было запроса на завершение
	for eventId != EventExitReq {
		// Ждем очередного события
		eventId, _ = waitForEvent(s.event, int(s.GetSleepTime()))
		if eventId == EventTimeout {
			// Если вышли по таймауту бездействия, то вызываем соответствуюший обработчик
			worker.OnIdle(s)
		} else if eventId > EventWake {
			// Иначе выполняем полезную работу
			worker.OnDoWork(eventId, s)
		}
		s.SetState(StateReady)
	}

	// Завершаем работу сервиса, престоповая работа
	err = s.worker.PostWork()
	if err != nil {
		print(s.GetName() + ": Error during postwork\n")
	}
}
